Instrucciones¶
Diseñar un flujo de procesamiento a nivel abstracto: adquisición de imágenes, pre-procesamiento, procesamiento de imágenes, cálculo de la medición, almacenamiento.
Con la base de datos seleccionada, implementar un sistema de reconocimiento y/o procesamiento en el cual se realice una medida o inferencia sobre imagen. Puede ser la detección de un objeto, medición de distancias entre píxeles de alguna característica, medición del tamaño de algún objeto (en cantidad de píxeles) clasificación de la imagen, entre otros.
Para procesar el conjunto completo de imágenes, medir los tiempos de ejecución del proceso completo. Realizar la medición para el 50% y el 100% de la imágenes varias veces, de manera de obtener algunas figuras estadísticas: tiempo máximo, tiempo mínimo, tiempo medio, desviación estándar del tiempo.
Hacer resumen de los resultados del procesamiento. Para ello debe decidir una métrica para indicar si el procesamiento es satisfactorio. Para esto último algunas veces es necesario hacer la evaluación en forma manual, por lo tanto, si fuera este el caso, seleccionar un sub-conjunto de las imágenes (por ejemplo, unas 50) y comparar el desempeño del algoritmo en estas imágenes.
🔄 Flujo del Proyecto de Clasificación de Imágenes Satelitales¶
[1] Adquisición de Imágenes
│
▼
[2] Pre-procesamiento
├── Extracción de rutas y etiquetas
├── Redimensionamiento (224x224)
├── Normalización de píxeles
├── Balanceo de clases (Random Over-Sampling)
├── División en train/val/test
└── Aumento de datos
│
▼
[3] Procesamiento de Imágenes
└── Entrenamiento de modelos CNN
(VGG16, VGG19, InceptionV3, Xception)
│
▼
[4] Cálculo de la Medición / Inferencia
├── Predicción de clase
└── Evaluación de métricas
(Precisión, Recall, F1, Matriz de confusión)
│
▼
[5] Almacenamiento
└── Guardar modelos entrenados (.h5)
Satellite Image Classification - Proyecto Computer Vision¶
Introducción¶
Este proyecto tiene como objetivo clasificar imágenes satelitales utilizando redes neuronales convolucionales (CNN). El dataset contiene imágenes categorizadas que han sido recopiladas desde Kaggle, y será usado para entrenar un modelo de clasificación multiclase.
El código implementa un flujo de trabajo completo para la clasificación de imágenes de teledetección, desde la carga y preprocesamiento de datos hasta el entrenamiento y evaluación de varios modelos de aprendizaje profundo basados en transferencia de aprendizaje.
0. Configuración de rutas locales¶
import tensorflow as tf
import sklearn
import numpy as np
print("NumPy:", np.__version__)
print("TensorFlow:", tf.__version__)
print("Scikit-learn:", sklearn.__version__)
print("GPU:", tf.config.list_physical_devices('GPU'))
NumPy: 1.26.4 TensorFlow: 2.10.0 Scikit-learn: 1.6.1 GPU: [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
import kagglehub
# Download latest version
path = kagglehub.dataset_download("umeradnaan/remote-sensing-satellite-images")
print("Path to dataset files:", path)
c:\Users\cesar.godoy\anaconda3\envs\ComputerVision\lib\site-packages\tqdm\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html from .autonotebook import tqdm as notebook_tqdm
Path to dataset files: C:\Users\cesar.godoy\.cache\kagglehub\datasets\umeradnaan\remote-sensing-satellite-images\versions\1
from pathlib import Path
# Ruta base local al dataset
base_dir = Path(r"C:\Users\cesar.godoy\.cache\kagglehub\datasets\umeradnaan\remote-sensing-satellite-images\versions\1\Remote Sensing Data.v2i.yolov8")
# Rutas corregidas
image_dir = base_dir / "train" / "images"
label_dir = base_dir / "train" / "labels"
print("📁 Imágenes:", image_dir)
print("📁 Etiquetas:", label_dir)
📁 Imágenes: C:\Users\cesar.godoy\.cache\kagglehub\datasets\umeradnaan\remote-sensing-satellite-images\versions\1\Remote Sensing Data.v2i.yolov8\train\images 📁 Etiquetas: C:\Users\cesar.godoy\.cache\kagglehub\datasets\umeradnaan\remote-sensing-satellite-images\versions\1\Remote Sensing Data.v2i.yolov8\train\labels
1. Carga y Exploración Inicial de Datos:¶
Se cargan las rutas de las imágenes y sus etiquetas correspondientes. Se crea un DataFrame de Pandas para organizar la información y se realiza una exploración inicial para entender la estructura de los datos, incluyendo el tamaño, las columnas, la detección de duplicados y valores faltantes.
image_files = list(image_dir.glob("*.jpg"))
label_files = list(label_dir.glob("*.txt"))
print(f"Total imágenes: {len(image_files)}")
print(f"Total labels: {len(label_files)}")
Total imágenes: 700 Total labels: 700
import os
import pandas as pd
image_paths = []
class_labels = []
center_x = []
center_y = []
width = []
height = []
for img, lbl in zip(image_files, label_files):
img_path = os.path.join(image_dir, img)
label_path = os.path.join(label_dir, lbl)
with open(label_path, 'r') as file:
for line in file:
parts = line.strip().split()
if len(parts) == 5:
image_paths.append(img_path)
class_labels.append(int(parts[0]))
center_x.append(float(parts[1]))
center_y.append(float(parts[2]))
width.append(float(parts[3]))
height.append(float(parts[4]))
df = pd.DataFrame({
'image_path': image_paths,
'class_label': class_labels,
'center_x': center_x,
'center_y': center_y,
'width': width,
'height': height
})
df.head()
| image_path | class_label | center_x | center_y | width | height | |
|---|---|---|---|---|---|---|
| 0 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 2 | 0.498437 | 0.498437 | 0.996094 | 0.996094 |
| 1 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 10 | 0.510938 | 0.618750 | 0.970313 | 0.754687 |
| 2 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 9 | 0.498437 | 0.498437 | 0.996094 | 0.996094 |
| 3 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 4 | 0.498437 | 0.498437 | 0.996094 | 0.996094 |
| 4 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 5 | 0.498437 | 0.498437 | 0.996094 | 0.996094 |
df.tail()
| image_path | class_label | center_x | center_y | width | height | |
|---|---|---|---|---|---|---|
| 743 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 7 | 0.498437 | 0.498437 | 0.996094 | 0.996094 |
| 744 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 3 | 0.498437 | 0.498437 | 0.996094 | 0.996094 |
| 745 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 2 | 0.498437 | 0.498437 | 0.996094 | 0.996094 |
| 746 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 3 | 0.182031 | 0.498437 | 0.364063 | 0.996094 |
| 747 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 11 | 0.662500 | 0.498437 | 0.667969 | 0.996094 |
df.shape
(748, 6)
df.columns
Index(['image_path', 'class_label', 'center_x', 'center_y', 'width', 'height'], dtype='object')
df.duplicated().sum()
0
df.isnull().sum()
image_path 0 class_label 0 center_x 0 center_y 0 width 0 height 0 dtype: int64
df = df[['image_path', 'class_label']]
df
| image_path | class_label | |
|---|---|---|
| 0 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 2 |
| 1 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 10 |
| 2 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 9 |
| 3 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 4 |
| 4 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 5 |
| ... | ... | ... |
| 743 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 7 |
| 744 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 3 |
| 745 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 2 |
| 746 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 3 |
| 747 | C:\Users\cesar.godoy\.cache\kagglehub\datasets... | 11 |
748 rows × 2 columns
2. Visualización de Datos:¶
Se visualiza la distribución de las clases mediante gráficos de barras y gráficos circulares para identificar posibles desequilibrios en el conjunto de datos. También se muestran imágenes de ejemplo para cada categoría.
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(8, 6))
ax = sns.countplot(data=df, x='class_label', palette="viridis")
plt.title('Count Plot of Each Category')
plt.xlabel('Class Label')
plt.ylabel('Count')
for p in ax.patches:
ax.annotate(f'{int(p.get_height())}',
(p.get_x() + p.get_width() / 2, p.get_height()),
ha='center', va='bottom', fontsize=12, color='black')
plt.show()
plt.figure(figsize=(8, 8))
class_counts = df['class_label'].value_counts()
plt.pie(class_counts, labels=class_counts.index, autopct='%1.1f%%', colors=sns.color_palette("viridis"), startangle=140)
plt.title('Pie Chart of Each Category')
plt.show()
C:\Users\cesar.godoy\AppData\Local\Temp\5\ipykernel_30508\4022176797.py:5: FutureWarning: Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect. ax = sns.countplot(data=df, x='class_label', palette="viridis")
import random
from PIL import Image
images_per_category = 5
class_labels = df['class_label'].unique()
fig, axes = plt.subplots(len(class_labels), images_per_category, figsize=(15, 3 * len(class_labels)))
for i, class_label in enumerate(class_labels):
class_images = df[df['class_label'] == class_label]['image_path'].tolist()
sampled_images = random.sample(class_images, min(images_per_category, len(class_images)))
for j, image_path in enumerate(sampled_images):
img = Image.open(image_path)
axes[i, j].imshow(img)
axes[i, j].axis('off')
if j == 0:
axes[i, j].set_ylabel(f"Class {class_label}", rotation=0, labelpad=50, fontsize=12, ha='right', va='center')
plt.tight_layout()
plt.show()